package com.xg.sys.controller;

import com.xg.sys.service.SysCaptchaService;
import com.xg.utils.RSAEncrypt;
import com.xg.frame.constant.Constant;
import com.xg.frame.constant.SysConstant;
import com.xg.frame.shiro.JwtUtil;
import com.xg.sys.entity.SysUser;
import com.xg.sys.service.SysMenuService;
import com.xg.sys.service.SysUserService;
import com.xg.sys.vo.SysMenuVO;
import com.xg.frame.constant.UrlConstant;
import com.xg.frame.entity.R;
import com.xg.frame.log.annotation.LogAnnotation;
import com.xg.frame.utils.MD5Utils;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.shiro.SecurityUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author tzt
 * @date Created in 2021/8/5 16:30
 * @description：登录相关
 * @modified By：
 */
@Controller
@Slf4j
@RequestMapping
public class LoginController {
    @Resource
    private SysUserService sysUserService;
    @Resource
    private SysMenuService sysMenuService;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private SysCaptchaService sysCaptchaService;
    // 允许失败的次数
    private static final Integer FAILED_TIMES_LOCK = 5;
    // 锁定时长
    private static final Integer FAILED_LOCK_TIME = 20 * 60 * 1000; //20分钟

    /**
     * 用户登录
     *
     * @param username
     * @param password
     * @param response
     * @return
     * @throws Exception
     */
    @LogAnnotation(module = UrlConstant.sysCenter, describe = "用户登录")
    @RequestMapping("/sys/login")
    @ResponseBody
    public R login(String username, String password, String captcha, String uuid, HttpServletRequest request, HttpServletResponse response, Model model) throws Exception {

        if (!sysCaptchaService.validate(uuid, captcha)) {
            return R.error().message("验证码错误");
        }

        try {
            // 获取用户
            List<SysUser> userList = sysUserService.getByUserName(username);
            if (CollectionUtils.isEmpty(userList)) {
                log.info("用户：{}不存在", username);
                return R.error().message("用户或密码错误");
            }
            SysUser user = userList.get(0);

            if (SysConstant.STATUS_N.equals(user.getStatus())) {
                log.info("用户：{}已停用", username);
                return R.error().message("用户已停用");
            }

            // 必须在校验密码之前，否则可以试出密码
            if (user.getLockTime() != null && new Date().before(user.getLockTime())) {
                log.info("用户已锁定，解锁时间：{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(user.getLockTime()));
                return R.error().message("用户已锁定");
            }

            // 校验密码
            if (!user.getPassword().equals(MD5Utils.stringToMD5(decrypt(password)))) {
                // 锁定时间已过，重置失败次数
                Boolean resetErrNum = user.getLockTime() != null && new Date().after(user.getLockTime()) && user.getErrNum() >= FAILED_TIMES_LOCK;

                // 增加失败次数
                Integer errNum = user.getErrNum() == null || resetErrNum ? 0 : user.getErrNum();
                SysUser errUser = new SysUser().setId(user.getId()).setErrNum(errNum + 1);
                if (errUser.getErrNum() >= FAILED_TIMES_LOCK) {
                    errUser.setLockTime(new Date(System.currentTimeMillis() + FAILED_LOCK_TIME));
                }
                sysUserService.updateById(errUser);

                log.info("用户：{}，密码校验失败！", username);
                return R.error().message(
                        errUser.getErrNum() >= FAILED_TIMES_LOCK ?
                                "已达到最大失败次数，用户已锁定" :
                                "用户或密码错误,再失败" + (FAILED_TIMES_LOCK - errUser.getErrNum()) + "次锁定用户"
                );
            }

            // 登陆
            String token = JwtUtil.sign(String.valueOf(user.getId()));
            redisTemplate.opsForValue().set(SysConstant.CATCHE_TOKEN + token, token, JwtUtil.expire * 2, TimeUnit.SECONDS);
            Cookie cookie = new Cookie("token", token);
            cookie.setMaxAge(604800);
            cookie.setHttpOnly(true);
            cookie.setPath("/");
            response.addCookie(cookie);

            // 重置失败次数
            SysUser loginUser = new SysUser().setId(user.getId()).setErrNum(0);
            if (user.getLoginTime() != null) {
                // 设置登陆时间和登陆IP
                loginUser.setLoginTime(new Date()).setLoginIp(request.getRemoteAddr());
            }
            sysUserService.updateById(loginUser);

            return R.ok().info(loginUser.getId());
        } catch (Exception e) {
            log.error("登录失败！", e);
            return R.error().message("登录失败");
        }
    }

    private String decrypt(String password) throws Exception {
        String pwd = RSAEncrypt.decrypt(password);
        String array[] = pwd.split("-");
        if (array != null && array.length == 2) {
            password = array[0];
//                    String loginTime = array[1];
//                    Long second  = System.currentTimeMillis() - Long.valueOf(loginTime);
//                    if(second>3600*1000){
//                        return R.error().message("登录密钥异常");
//                    }
        }
        return password;
    }


    @ApiOperation(value = "验证码")
    @GetMapping("sys/login/captcha.jpg")
    public void captcha(HttpServletResponse response, String uuid) throws Exception {
        response.setHeader("Cache-Control", "no-store, no-cache");
        response.setContentType("image/jpeg");

        //获取图片验证码
        BufferedImage image = sysCaptchaService.getCaptcha(uuid);

        ServletOutputStream out = response.getOutputStream();
        ImageIO.write(image, "jpg", out);
        IOUtils.closeQuietly(out);
    }

    @RequestMapping("/main")
    public String main(Model model) throws Exception {
        SysUser currentUser = (SysUser) SecurityUtils.getSubject().getPrincipal();
        List<SysMenuVO> currentUserMenus = sysMenuService.getCurrentUserMenus(currentUser.getId());
        model.addAttribute("menuList", currentUserMenus);
        model.addAttribute("userName", currentUser.getSurname());
        model.addAttribute("firstLogin", currentUser.getLoginTime() == null);
        model.addAttribute("userId", String.valueOf(currentUser.getId()));
        return "main";
    }

    @ApiOperation(value = "登录注销", notes = "登录注销")
    @LogAnnotation(module = UrlConstant.sysCenter, describe = "登录注销")
    @GetMapping("/logout")
    public String logout(HttpServletRequest request, HttpServletResponse response) {
        //删除Cookie
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie c : cookies) {
                if (c.getName().equals(Constant.token)) {
                    Cookie cookie = new Cookie(c.getName(), null);//删除前必须要new 一个valu为空；
                    cookie.setPath("/");//路径要相同
                    cookie.setMaxAge(0);//生命周期设置为0
                    response.addCookie(cookie);
                    break;
                }
            }
        }
        return "login";
    }

    /**
     * 首页
     */
    @RequestMapping("/sy")
    public String sy(Model model) throws Exception {
        return "sy";
    }

    @RequestMapping
    public String login() throws Exception {
        return "login";
    }
}
